// // Copyright (c) 2009 All Right Reserved // // vl // // 2009-02-01 // using System; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Globalization; using System.Linq; using System.Text; using System.Xml.Linq; using JetBrains.Annotations; using LargoCommon.Abstract; namespace LargoCommon.Music { /// /// Musical Strip. /// public class MusicalStrip { #region Fields /// /// Musical Lines. /// private IList lines; #endregion #region Constructors /// /// Initializes a new instance of the class. /// /// The given context. public MusicalStrip(MusicalContext givenContext) : this() { this.Context = givenContext; } /// /// Initializes a new instance of the class. /// public MusicalStrip() { this.lines = new List(); } /// /// Initializes a new instance of the class. /// /// The mark strip. /// The given context. public MusicalStrip(XElement markStrip, MusicalContext givenContext) : this() { Contract.Requires(markStrip != null); if (markStrip == null) { return; } this.Context = givenContext; XElement xlines = markStrip.Element("Lines"); if (xlines == null) { return; } var header = givenContext.Header; var xelements = xlines.Elements(); foreach (XElement xline in xelements) { MusicalLine line = new MusicalLine(xline, header) { Strip = this }; this.AddLine(line, false); } } #endregion #region Properties - Xml /// Gets Xml representation. /// Property description. public virtual XElement GetXElement { get { var xstrip = new XElement( "Strip", new XAttribute("HasLines", this.HasAnyMusicalLine)); //// Lines var xlines = new XElement("Lines"); //// Musical Lines to XML foreach (MusicalLine line in this.Lines.Where(line => line != null)) { //// line.MusicalBlock = givenMusicalBlock; var xline = line.GetXElement; xlines.Add(xline); } xstrip.Add(xlines); return xstrip; } } #endregion #region Properties /// /// Gets or sets the context. /// /// /// The context. /// public MusicalContext Context { get; set; } /// /// Gets Musical Lines. /// /// Property description. public IList Lines { get { Contract.Ensures(Contract.Result>() != null); if (this.lines == null) { throw new InvalidOperationException("Musical lines are null."); } return this.lines; } //// Remove private set - DevExpress private set => this.lines = value; } /* Temporary - old code start .... */ /// /// Gets or sets NumberOfBars. /// /// General musical property. public byte NumberOfMelodicLines { get; set; } /// /// Gets or sets NumberOfBars. /// /// General musical property. public byte NumberOfRhythmicLines { get; set; } /* Temporary - old code end .... */ /// /// Gets a value indicating whether HasMusicalLine. /// /// General musical property. public bool HasAnyMusicalLine { get { var exists = (from mt in this.Lines where !mt.IsEmpty select 1).Any(); //// mt.IsSelected && return exists; } } #endregion #region Public methods /// /// Gets lines having purpose. /// /// The purpose. /// /// Returns value. /// public IList LinesHavingPurpose(LinePurpose purpose) { var ls = (from line in this.Lines where line.Purpose == purpose select line).ToList(); return ls; } /// /// Sets the lines. /// /// The given lines. public void SetLines(IList givenLines) { this.lines = givenLines; } /// /// Writes the body. /// /// The given body. public void WriteBody(MusicalBody givenBody) { this.Context = givenBody.Context; foreach (var line in this.lines) { line.Strip = this; //// 2019/11 !?! line.Tones.Clear(); } foreach (var bar in givenBody.Bars) { foreach (var element in bar.Elements) { //// if (bar.BarNumber == 1 && element.Status != null) { //// element.Status.Channel = element.Line.MainVoice.Channel; //// ?!? } foreach (var tone in element.Tones) { element.MusicalLine.Tones.Add(tone); } } } } /// /// Deletes the line. /// /// The line number. public void DeleteLine(int lineIndex) { if (lineIndex >= this.Lines.Count) { return; } var line = this.Lines[lineIndex]; this.Lines.Remove(line); this.RenumberLines(); } /// /// Deletes the line. /// /// The line identifier. public void DeleteLine(Guid lineIdent) { var line = this.GetLine(lineIdent); if (line != null) { this.Lines.Remove(line); this.RenumberLines(); } } /// String with line details. /// Returns value. [UsedImplicitly] public string LinesToString() { var s = new StringBuilder(); s.Append(" Line Tones \n"); s.Append("-------------------------------------------------------------------\n"); var musicLines = from p in this.Lines orderby p.LineIndex descending select p; foreach (var mline in musicLines.Where(mline => mline?.Tones != null).Where(mline => !mline.IsEmpty)) { s.Append(mline + "\r\n\n"); } return s.ToString(); } /// /// Finds the free channel. /// /// The line number. /// /// Returns value. /// public MidiChannel FindFreeChannel(int lineIndex) { //// int barNumber //// Exclude not used lines like LinePurpose.Mute (?!) var channels = (from line in this.Lines where line.LineIndex != lineIndex select line.MainVoice.Channel).Distinct().ToList(); for (byte channel = 0; channel <= MusicalProperties.MidiCountChannels; channel++) { var midiChannel = (MidiChannel)channel; if (midiChannel != MidiChannel.DrumChannel && !channels.Contains(midiChannel)) { return midiChannel; } } var c = (MidiChannel)(lineIndex % 16); if (c != MidiChannel.DrumChannel) { return c; } return MidiChannel.C00; } /// /// Clones the specified include tones. /// /// if set to true [include tones]. /// Returns value. public MusicalStrip Clone(bool includeTones) { var strip = (MusicalStrip)this.Clone(); strip.Lines = new List(); foreach (var newLine in this.Lines.Select(line => line.Clone(includeTones))) { //// newLine.IsComposed = false; var ts = (List)strip.Lines; ts.Add(newLine); } return strip; } /// /// Remove the lines. /// public void ResetLines() { this.lines = new List(); } /// /// Resets the tones. /// public void ResetTones() { foreach (var line in this.lines) { line.SetTones(new MusicalStrikeCollection()); } } /// /// Gets the musical line. /// /// The line number. /// Returns value. [UsedImplicitly] public MusicalLine GetLine(int lineIndex) { //// bool exists = (from mt in this.MusicalLines where mt.IsSelected && !mt.IsEmpty select 1).Any(); if (lineIndex < 0 || lineIndex >= this.Lines.Count) { return null; } var ts = (List)this.Lines; return ts[lineIndex]; } /// /// Gets the line. /// /// The line identifier. /// Returns value. [UsedImplicitly] public MusicalLine GetLine(Guid lineIdent) { var ts = (List)this.Lines; var line = (from t in ts where t.LineIdent == lineIdent select t).FirstOrDefault(); return line; } /// /// Add Musical Line. /// /// Musical Line. /// if set to true [renumber]. public void AddLine(MusicalLine line, bool renumber) { if (line == null) { return; } if (renumber) { //// if (line.LineIndex == 0) { 2016/07 var num = this.Lines.Count; line.LineIndex = (byte)num; //// } //// 2016/07 //// 2015/01 //// if (line.LineIndex == 0) { line.LineIndex = line.LineIndex; } } var ts = (List)this.Lines; ts.Add(line); this.Context.Header.NumberOfLines = (byte)ts.Count; } /// /// Removes the line. /// /// The line. [UsedImplicitly] public void RemoveLine(MusicalLine line) { if (line == null) { return; } var ts = (List)this.Lines; ts.Remove(line); } /// /// Moves the tones from edges. /// /// The min note. /// The max note. public void MoveTonesFromEdges(byte minNote, byte maxNote) { if (this.Lines == null || !this.Lines.Any()) { return; } // ReSharper disable once LoopCanBePartlyConvertedToQuery foreach (var mt in this.Lines.SelectMany(line => line.Tones.OfType().Where(mt => !mt.IsPause))) { if (mt.IsTrueTone) { mt.Pitch.MoveFromEdges(minNote, maxNote); } } } /// /// Corrects the octaves. /// public void CorrectOctaves() { foreach (var line in this.Lines) { OrchestraChecker.Singleton.CorrectOctavesOfInstrumentedTones(line.Tones); } var setup = MusicalSettings.Singleton; this.MoveTonesFromEdges(setup.NoteLowest, setup.NoteHighest); } /// /// Rebuild Channels. /// public void RebuildChannels() { if (this.Lines.Count <= 15) { foreach (var line in this.Lines) { line.MainVoice.Channel = line.FirstStatus.IsMelodic ? MusicalProperties.ChannelForPartNumber(line.LineIndex) : MidiChannel.DrumChannel; } return; } if (this.Lines.Count <= 30) { foreach (var line in this.Lines) { line.MainVoice.Channel = line.FirstStatus.IsMelodic ? MusicalProperties.ChannelForPartNumber(line.LineIndex / 2) : MidiChannel.DrumChannel; } } } /// /// Splits the same tones. /// [UsedImplicitly] public void SplitTheFollowUpTones() { if (this.Lines == null || !this.Lines.Any()) { return; } foreach (var line in this.Lines) { SplitTheFollowUpTonesInLine(line); } } #endregion #region Public methods - Transformation /// /// Total Horizontal Inversion. /// [UsedImplicitly] public void HorizontalInversion() { if (this.Lines == null || !this.Lines.Any()) { return; } this.Lines.ForAll(musicalLine => musicalLine.TotalHorizontalInversion()); } /// /// Total Vertical Inversion. /// [UsedImplicitly] public void VerticalInversion() { if (this.Lines == null || !this.Lines.Any()) { return; } // ReSharper disable once LoopCanBePartlyConvertedToQuery foreach (var mt in this.Lines.SelectMany(line => line.Tones.OfType().Where(mt => !mt.IsPause))) { mt.Pitch?.SetAltitude(128 - mt.Pitch.SystemAltitude); } } /// /// Bar Horizontal Inversion. /// [UsedImplicitly] public void BarInversion() { if (this.Lines == null || !this.Lines.Any()) { return; } this.Lines.ForAll(musicalLine => musicalLine.BarHorizontalInversion()); } /// /// Modular Deformation. /// [UsedImplicitly] public void ModularDeformation() { if (this.Lines == null || !this.Lines.Any()) { return; } const int deformation = 5; var absDeformation = Math.Abs(deformation); if (absDeformation <= 0) { return; } foreach (var line in this.Lines) { foreach (var tone in line.Tones) { MusicalTone mt = tone as MusicalTone; if (mt == null || mt.IsPause) { continue; } var altitude = mt.Pitch.SystemAltitude; var steps = mt.Pitch.SystemAltitude % absDeformation; //// MusMath.RandomNatural(3); altitude = altitude + (Math.Sign(deformation) * steps); mt.Pitch.SetAltitude(altitude); } } } /// /// Vertical Extension. /// [UsedImplicitly] public void VerticalExtension() { if (this.Lines == null || !this.Lines.Any()) { return; } foreach (var line in this.Lines) { foreach (var tone in line.Tones) { //// PlannedTones MusicalTone mt = tone as MusicalTone; if (mt == null || mt.IsPause || mt.IsEmpty) { continue; } var altitude = mt.Pitch.SystemAltitude; altitude = Math.Max((2 * altitude) - DefaultValue.MeanNoteAltitude, 0); altitude = Math.Min(altitude, 127); mt.Pitch.SetAltitude(altitude); } } } /// /// Vertical Narrowing. /// [UsedImplicitly] public void VerticalNarrowing() { const byte nivelizationFactor = 2; const byte altitudeShift = 48; if (this.Lines == null || !this.Lines.Any()) { return; } foreach (var line in this.Lines) { foreach (var tone in line.Tones) { //// PlannedTones MusicalTone mt = tone as MusicalTone; if (mt == null || mt.IsPause || mt.IsEmpty) { continue; } var altitude = mt.Pitch.SystemAltitude; altitude = Math.Max((altitude / nivelizationFactor) + altitudeShift, 0); altitude = Math.Min(altitude, 127); mt.Pitch.SetAltitude(altitude); } } } /// /// Shifts Octave Up. /// [UsedImplicitly] public void OctaveUp() { if (this.Lines == null || !this.Lines.Any()) { return; } foreach (var line in this.Lines) { foreach (var mt in line.Tones.OfType().Where(mt => !mt.IsPause && !mt.IsEmpty)) { mt.Pitch.ShiftOctave(+1); } } } /// /// Shifts Octave Down. /// [UsedImplicitly] public void OctaveDown() { if (this.Lines == null || !this.Lines.Any()) { return; } foreach (var line in this.Lines) { foreach (var mt in line.Tones.OfType().Where(mt => !mt.IsPause && !mt.IsEmpty)) { mt.Pitch.ShiftOctave(-1); } } } #endregion #region String representation /// String representation of the object. /// Returns value. public override string ToString() { var s = new StringBuilder(); s.Append(string.Format(CultureInfo.CurrentCulture, "Lines {0}", this.Lines.Count)); return s.ToString(); } #endregion #region Tones /// List of tones in one bar of musical part. /// Number of musical bar. /// Returns value. [UsedImplicitly] public MusicalToneCollection MelodicTonesInBar(int barNumber) { var tonesInBar = new List(); foreach (var mtones in from line in this.Lines where line?.Tones != null //// && mline.IsSelected where line.FirstStatus.LineType == MusicalLineType.Melodic select line.MelodicTonesInBar(barNumber) into mtones where mtones != null && mtones.Any() select mtones) { tonesInBar.AddRange(mtones); } return new MusicalToneCollection(tonesInBar); } #endregion #region Private static methods /// /// Splits the follow up tones in line. /// /// The line. private static void SplitTheFollowUpTonesInLine(MusicalLine line) { Contract.Requires(line != null); MusicalTone lastLine = null; foreach (var mt in line.Tones.OfType().Where(mt => mt.IsTrueTone && !mt.IsPause)) { if (lastLine != null && lastLine.IsGoingToNextBar && lastLine.BarNumber == mt.BarNumber + 1 //// && lastLine.BarNumberTo == mt.BarNumber && mt.IsFromPreviousBar && lastLine.Pitch.SystemAltitude == mt.Pitch.SystemAltitude && lastLine.BitTo == mt.BitFrom - 1) { lastLine.Duration += mt.Duration; //// lastLine.BarNumberTo = mt.BarNumberTo; mt.Loudness = 0; } lastLine = mt; } } #endregion #region Private methods /// Makes a deep copy of the MusicalStrip object. /// Returns object. private object Clone() { var strip = new MusicalStrip(this.Context); return strip; } /// /// Renumbers the lines. /// private void RenumberLines() { int lineIndex = 0; foreach (var line in this.Lines) { line.LineIndex = lineIndex++; } } #endregion } }